package assignment1;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;


class MyServerMulti {
	
	/**
	 * 
	 */
	private int _port = 7000;
	private int _backlog = 5;
	private ServerSocket _server=null;
		
	public MyServerMulti() {
		
		/* Initialize socket */		
		try {			
			InetAddress addr = InetAddress.getLocalHost(); 
			System.out.println("IP: " + addr.getHostAddress());
			
			// all addresses on localhost
			_server = new ServerSocket(_port, _backlog, null );
			// server at local IP address
			// should open port in firewall settings
			//_server = new ServerSocket(_port, _backlog, InetAddress.getByName("192.168.1.103"));
			//_server = new ServerSocket(_port, _backlog, InetAddress.getLocalHost() );
		}
		catch (IOException ex) {			
			System.out.println(ex.toString());
			System.out.println("Could not listen on port " + _port);
		    System.exit(-1);
			//System.out.println(ex.toString());
		}			
		System.out.println("ServerSocket has been created on port "+ _port);
	}
	
	
	private void listenToMultiClients() {
	
		System.out.println("Listening for connections from clients ....");
			
		/* listen for connections from clients */	
		while (true) {
			
			try {
				ClientWorker clientWorker = new ClientWorker(_server.accept());
				System.out.println("New Connection has been established, ID = " + ClientWorker.ID);
				Thread th = new Thread(clientWorker);
				th.start();						
			}
			catch (IOException ex) {
				System.out.println("accept() failed at port " + _port);
				System.exit(-1);
			}			
		}																		
	}

	/*
	 * Called before exit. Cleans up ServerSocket objects.
	 * (non-Javadoc)
	 * @see java.lang.Object#finalize()
	 */
	protected void finalize() {
		/* Clean up*/
		try {
			_server.close();
		}
		catch (IOException ex) {
			System.out.println("socket Could not close.");
	        System.exit(-1);
		}
	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub

		MyServerMulti serverMulti = new MyServerMulti();
		serverMulti.listenToMultiClients();
		System.out.println("exit");
		System.exit(0);				// never reach here
	}
}


class ClientWorker implements Runnable {
	public static int ID=0;
	private int _id;
	private Socket _socket;	
	//BufferedReader _bufferReaderIn = null;
	//PrintWriter _printWriterOut = null;
	//OutputStreamWriter _outStream = null;
	DataOutputStream _dataOut = null;
	DataInputStream _dataIn = null;
		
	public ClientWorker(Socket socket) {
		_socket = socket;
		ID++;
		this._id = ID;
	}

	@Override
	public void run() {
				
		/* Get the socket's input and output streams */
		try {
			//_bufferReaderIn = new BufferedReader(new InputStreamReader(_socket.getInputStream()));
			//_outStream = new OutputStreamWriter(_socket.getOutputStream(  ));
			//_printWriterOut = new PrintWriter(_socket.getOutputStream(), true);
			//_outBuffer = new PrintWriter(new OutputStreamWriter(_sockIn.getOutputStream()));
										
			_dataIn = new DataInputStream(_socket.getInputStream());		// deprecated readLine()
			_dataOut = new DataOutputStream(_socket.getOutputStream());
		}
		catch (IOException ex) {
			System.out.println("getInput/OutputStream() failed.");
		    System.exit(-1);
		}
		
		/* Read data from client and send data back to client*/
		boolean quit=false;
		boolean found;
		while (!quit) {
			String line=null;		// text from client
			String ip;
			try {			
				System.out.println("Waiting data from client...");
				line = _dataIn.readUTF();						// readUTF() blocks until data's available
				found = _dataIn.readBoolean();
				ip = _dataIn.readUTF();
				System.out.println("Client sent: " + line);
				System.out.println("Found: " + found);
				System.out.println("IP: " + ip);
			}
			catch (IOException ex) {
				System.out.println("Read from client failed.");
		        System.exit(-1);
			}
			
			/* server receives and sends back to client */
			if (line != null) {
				if (!line.trim().toUpperCase().equals("QUIT")) {
					try {
						_dataOut.writeUTF("Server got it!");
						_dataOut.writeUTF(line);
//						_dataOut.writeUTF("Server got it! \r\n");						
//						_dataOut.writeUTF(line + "\r\n");
						_dataOut.flush();
//						_outStream.write("Server got it! \r\n");
//						_outStream.write(line +"\r\n");
//						_outStream.flush();
					}
					catch (IOException e) {
						System.out.println(e);
					}
					
//					_printWriterOut.println("Server got it !");		// send text backs to client
//					_printWriterOut.flush();
//					_printWriterOut.println(line);	
//					_printWriterOut.flush();
				}
				else {		/* client quits */
					System.out.println("Client " + this._id + " quit.");
					quit=true;
				}
			}
			else {
				System.out.println("Client sent null data.");
			}
		}
	}
	
	/*
	 * Called before exit. Cleans up ServerSocket objects.
	 * (non-Javadoc)
	 * @see java.lang.Object#finalize()
	 */
	protected void finalize() {
		/* Clean up*/
		try {
			//_bufferReaderIn.close();
			//_printWriterOut.close();
			_dataIn.close();
			_dataOut.close();
			_socket.close();
		}
		
		catch (IOException ex) {
			System.out.println("socket Could not close.");
	        System.exit(-1);
		}
	}
}
